/**
 * Title: Hyperbolic engine
 * Description: jFamilyTree Engine
 * Version: provider
 * Copyright: (c) 2001-2008
 * Terms of use:see license agreement at http://treebolic.sourceforge.net/en/license.htm
 * Author: Bernard Bou
 * Company: bsys
 * Update: Mon Mar 10 00:00:00 CEST 2008
 */
package jFamilyTree.view;

import java.awt.Dimension;
import java.awt.Point;

import jFamilyTree.core.Complex;
import jFamilyTree.model.INode;
import jFamilyTree.model.Location;

/**
 * Mapper of unit circle space to view
 * 
 * @author Bernard Bou
 */
public class Mapper
{
	/**
	 * Size of graphics context
	 */
	protected Dimension theSize;

	/**
	 * Scale x factor
	 */
	protected double theScaleX = 1.;

	/**
	 * Scale y factor
	 */
	protected double theScaleY = 1.;

	/**
	 * X shift
	 */
	protected double theXShift = 0.;

	/**
	 * Y shift
	 */
	protected double theYShift = 0.;

	/**
	 * Top coordinate
	 */
	protected int theTop;

	/**
	 * Left coordinate
	 */
	protected int theLeft;

	/**
	 * Zoom factor
	 */
	private double theZoomFactor = 1.;

	// S I Z E

	/**
	 * Get size
	 * 
	 * @return size
	 */
	public Dimension getSize()
	{
		return theSize;
	}

	// A C C E S S

	/**
	 * Set view zoom factor
	 * 
	 * @param thisFactor
	 *        zoom factor
	 */
	public void setZoomFactor(Double thisFactor)
	{
		theZoomFactor *= thisFactor;
	}

	/**
	 * Set view x-shift
	 * 
	 * @param thisX
	 *        view x-shift (0,1)
	 */
	public void setXShift(Double thisX)
	{
		theXShift = thisX;
	}

	/**
	 * Set view y-shift
	 * 
	 * @param thisY
	 *        view y-shift (0,1)
	 */
	public void setYShift(Double thisY)
	{
		theYShift = thisY;
	}

	/**
	 * Get view x-shift
	 * 
	 * @return view x-shift
	 */
	public double getXShift()
	{
		return theXShift;
	}

	/**
	 * Get view y-shift
	 * 
	 * @return view y-shift
	 */
	public double getYShift()
	{
		return theYShift;
	}

	/**
	 * Compute scale from view size
	 */
	protected void computeScale()
	{
		if (theSize == null)
			return;
		theScaleX = (0.5 + (theXShift > 0. ? theXShift : -theXShift)) * theSize.width;
		theScaleY = (0.5 + (theYShift > 0. ? theYShift : -theYShift)) * theSize.height;
	}

	// M A P . U N I T C I R C L E . T O . V I E W

	/**
	 * Convert unit circle x-coordinate to view x-coordinate
	 * 
	 * @param x
	 *        unit circle x-coordinate
	 * @return view x-coordinate
	 */
	protected int xUnitCircleToView(double x)
	{
		if (theSize == null)
			return 0;
		return (int) ((theScaleX * theZoomFactor * x) + (theXShift * theSize.width));
	}

	/**
	 * Convert unit circle y-coordinate to view y-coordinate
	 * 
	 * @param y
	 *        unit circle y-coordinate
	 * @return view y-coordinate
	 */
	protected int yUnitCircleToView(double y)
	{
		if (theSize == null)
			return 0;
		return (int) ((theScaleY * theZoomFactor * y) + (theYShift * theSize.width));
	}

	/**
	 * Convert unit circle x-extent to view x-extent
	 * 
	 * @param cx
	 *        unit circle x-extent
	 * @return view x-extent
	 */
	protected int wUnitCircleToView(double cx)
	{
		return (int) (theScaleX * theZoomFactor * cx);
	}

	/**
	 * Convert unit circle y-extent to view y-extent
	 * 
	 * @param cy
	 *        unit circle y-extent
	 * @return view y-extent
	 */
	protected int hUnitCircleToView(double cy)
	{
		return (int) (theScaleY * theZoomFactor * cy);
	}

	// M A P . V I E W . T O . U N I T C I R C L E

	/**
	 * Convert view x-coordinate to unit circle x-coordinate
	 * 
	 * @param vx
	 *        view x-coordinate
	 * @return unit circle x-coordinate
	 */
	protected double xViewToUnitCircle(double vx)
	{
		if (theSize == null)
			return 0.;
		return (vx - (theXShift * theSize.width)) / (theScaleX * theZoomFactor);
	}

	/**
	 * Convert view y-coordinate to unit circle y-coordinate
	 * 
	 * @param vy
	 *        view y-coordinate
	 * @return unit circle y-coordinate
	 */
	protected double yViewToUnitCircle(double vy)
	{
		if (theSize == null)
			return 0.;
		return (vy - (theYShift * theSize.height)) / (theScaleY * theZoomFactor);
	}

	/**
	 * Convert view x-extent (width) to unit circle x-extent
	 * 
	 * @param cvx
	 *        view x-extent
	 * @return unit circle x-extent
	 */
	protected double wViewToUnitCircle(double cvx)
	{
		return cvx / (theScaleX * theZoomFactor);
	}

	/**
	 * Convert view y-extent (height) to unit circle y-extent
	 * 
	 * @param cvy
	 *        view y-extent
	 * @return unit circle y-extent
	 */
	protected double hViewToUnitCircle(double cvy)
	{
		return cvy / (theScaleY * theZoomFactor);
	}

	// S P A C E . C O N V E R S I O N

	/**
	 * Convert view coordinates to unit circle coordinates
	 * 
	 * @param vx
	 *        x coordinate in view space
	 * @param vy
	 *        y coordinate in view space
	 * @param thisSize
	 *        view size
	 * @return point in unit circle
	 */
	public Complex viewToUnitCircle(int vx, int vy, Dimension thisSize)
	{
		Complex p = new Complex(vx, vy);

		// this offsets for translation on cache graphics
		p.set(p.re - thisSize.width / 2, p.im - thisSize.height / 2);

		// convert
		p.set(xViewToUnitCircle(p.re), yViewToUnitCircle(p.im));

		// adjust
		if (p.abs2() > 1.)
		{
			p.normalize();
			p.multiply(.99);
		}
		return p;
	}

	/**
	 * Get view coordinates of node
	 * 
	 * @param thisNode
	 *        node
	 * @return view coordinate of node
	 */
	public Point getViewLocation(INode thisNode)
	{
		Location thisLocation = thisNode.getLocation();
		return new Point(xUnitCircleToView(thisLocation.euclidian.center.re) - theLeft, yUnitCircleToView(thisLocation.euclidian.center.im) - theTop);
	}
}
